package cn.demoncat.util.es.util;

import cn.demoncat.util.es.constant.EsConstant;
import cn.demoncat.util.lang.LocalDateUtil;
import cn.demoncat.util.lang.ParamEmptyUtil;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;

import java.util.Collection;
import java.util.Map;


/**
 * ElasticSearch Criteria 工具
 * 
 * @author 延晓磊
 *
 * @since 2020-9-3
 */
public class CriteriaUtil {

	/**
	 * 查询全部
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-2
	 */
	public static Criteria all() {
		return new Criteria();
	}
	
	/**
	 * 增加条件的计分因子	
	 * 
	 * @param cri	条件参数
	 * @param boost	计分因子，匹配的数据的分值将乘以因子，取值为 [0 ~ 减分 ~ 1 ~ 加分]
	 * @return	
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria boost(Criteria cri, float boost) {
		return cri.boost(boost);
	}
	
	/**
	 * 设为否定条件 - [! cri]
	 * 
	 * @param cri	must_not: [cri]
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria not(Criteria cri) {
		return new Criteria().not().and(cri);
	}
	
	/**
	 * 添加可选条件 - [cri1 || cri2]
	 * 
	 * @param cri		条件参数
	 * @param other		其它条件：should: [cri1, cri2]
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年9月3日
	 */
	public static Criteria or(Criteria cri, Criteria other) {
		return cri.or(other);
	}
	
	/**
	 * 条件链 - [cri1 && cri2]
	 * 
	 * @param cris	must: [cri]
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria and(Criteria... cris) {
		Criteria criteria = new Criteria();
		for (Criteria c : cris) {
			if (c != null) {
				criteria = criteria.and(c);
			}
		}
		return criteria;
	}
	
	/**
	 * 条件链 - [cri1 && cri2]
	 * 
	 * @param cri	
	 * @param others must: [cri, other]
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria andOthers(Criteria cri, Criteria... others) {
		for (Criteria c : others) {
			if (cri != null) {
				cri = cri.and(c);
			}
		}
		return cri;
	}
	
	/**
	 * 字段 - 大于
	 * 
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria gt(String field, Object value) {
		return Criteria.where(field).greaterThan(value);
	}
	
	/**
	 * 字段 - 大于
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria gt(Criteria cri, String field, Object value) {
		return cri.and(field).greaterThan(value);
	}
	
	/**
	 * 字段 - 大于等于
	 * 
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria gte(String field, Object value) {
		return Criteria.where(field).greaterThanEqual(value);
	}
	
	/**
	 * 字段 - 大于等于
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria gte(Criteria cri, String field, Object value) {
		return cri.and(field).greaterThanEqual(value);
	}
	
	/**
	 * 字段 - 等于
	 * 
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria is(String field, Object value) {
		return Criteria.where(field).is(value);
	}
	
	/**
	 * 字段 - 等于
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria is(Criteria cri, String field, Object value) {
		return cri.and(field).is(value);
	}
	
	/**
	 * 字段 - 等于
	 * 
	 * @param kvs		字段值对
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria is(Map<String, Object> kvs) {
		Criteria cri = new Criteria();
		for (Map.Entry<String, Object> kv : kvs.entrySet()) {
			if (ParamEmptyUtil.isNotEmpty(kv.getValue())) {
				cri = cri.and(kv.getKey()).is(kv.getValue());
			}
		}
		return cri;
	}
	
	/**
	 * 字段 - 等于
	 * 
	 * @param cri		条件参数
	 * @param kvs		字段值对
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria is(Criteria cri, Map<String, Object> kvs) {
		for (Map.Entry<String, Object> kv : kvs.entrySet()) {
			if (ParamEmptyUtil.isNotEmpty(kv.getValue())) {
				cri = cri.and(kv.getKey()).is(kv.getValue());
			}
		}
		return cri;
	}
	
	/**
	 * 字段 - 小于
	 * 
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria lt(String field, Object value) {
		return Criteria.where(field).lessThan(value);
	}
	
	/**
	 * 字段 - 小于
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria lt(Criteria cri, String field, Object value) {
		return cri.and(field).lessThan(value);
	}
	
	/**
	 * 字段 - 小于等于
	 * 
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria lte(String field, Object value) {
		return Criteria.where(field).lessThanEqual(value);
	}
	
	/**
	 * 字段 - 小于等于
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria lte(Criteria cri, String field, Object value) {
		return cri.and(field).lessThanEqual(value);
	}
	
	/**
	 * 字段 - 区间
	 * 
	 * @param field	字段名：数值，日期，字符串（性能低）
	 * @param min	最小值（包含）
	 * @param max	最大值
	 * 
	 * @return min和max都为空时返回null
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria between(String field, Object min, Object max) {
		if (min == null && max == null) {
			return null;
		}
		if (min == null) {
			return lt(field, max);
		}
		if (max == null) {
			return gte(field, min);
		}
		return new Criteria(field).between(min, max);
	}
	
	/**
	 * 字段 - 区间
	 * 
	 * @param cri		条件参数
	 * @param field		字段名：数值，日期，字符串（性能低）
	 * @param min		最小值（包含）
	 * @param max		最大值
	 * 
	 * @return min和max都为空时返回cri
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria between(Criteria cri, String field, Object min, Object max) {
		if (min == null && max == null) {
			return cri;
		}
		if (min == null) {
			return lt(cri, field, max);
		}
		if (max == null) {
			return gte(cri, field, min);
		}
		return cri.and(field).between(min, max);
	}
	
	/**
	 * 时间区间
	 * 
	 * @param field
	 * @param startTime
	 * @param endTime
	 * @return 时间都为空则返回null
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年5月1日
	 */
	public static Criteria timeRange(String field, String startTime, String endTime) {
		return between(field, LocalDateUtil.toLocalDateTime(startTime), LocalDateUtil.toLocalDateTime(endTime));
	}
	
	/**
	 * 时间区间
	 * 
	 * @param cri	条件参数
	 * @param field
	 * @param startTime
	 * @param endTime
	 * @return 时间都为空则返回cri
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年5月1日
	 */
	public static Criteria timeRange(Criteria cri, String field, String startTime, String endTime) {
		return between(cri, field, LocalDateUtil.toLocalDateTime(startTime), LocalDateUtil.toLocalDateTime(endTime));
	}
	
	/**
	 * 字段 - 在列表中
	 * 
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria in(String field, Object... value) {
		return Criteria.where(field).in(value);
	}
	
	/**
	 * 字段 - 在列表中
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria in(Criteria cri, String field, Object... value) {
		return cri.and(field).in(value);
	}
	
	/**
	 * 字段 - 不在列表中
	 * 
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria nin(String field, Object... value) {
		return Criteria.where(field).notIn(value);
	}
	
	/**
	 * 字段 - 不在列表中
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		比较值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria nin(Criteria cri, String field, Object... value) {
		return cri.and(field).notIn(value);
	}
	
	/**
	 * 字段 - 包含
	 * 
	 * @param field		字段名
	 * @param value		值：%x%
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria contains(String field, String value) {
		return Criteria.where(field).contains(value);
	}
	
	/**
	 * 字段 - 包含
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		值：%x%
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria contains(Criteria cri, String field, String value) {
		return cri.and(field).contains(value);
	}
	
	/**
	 * 字段 - 后缀匹配
	 * 
	 * @param field		字段名
	 * @param value		值：%x
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria endsWith(String field, String value) {
		return Criteria.where(field).endsWith(value);
	}
	
	/**
	 * 字段 - 后缀匹配
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		值：%x
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria endsWith(Criteria cri, String field, String value) {
		return cri.and(field).endsWith(value);
	}
	
	/**
	 * 字段 - 前缀匹配
	 * 
	 * @param field		字段名
	 * @param value		值：x%
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria startsWith(String field, String value) {
		return Criteria.where(field).startsWith(value);
	}
	
	/**
	 * 字段 - 前缀匹配
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		值：x%
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria startsWith(Criteria cri, String field, String value) {
		return cri.and(field).startsWith(value);
	}
	
	/**
	 * 字段 - 模糊匹配（在包含的基础上，允许出现少数错别字，即相似词搜索）
	 * 
	 * @param field		字段名
	 * @param value		值：%x%
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria fuzzy(String field, String value) {
		return Criteria.where(field).fuzzy(value);
	}
	
	/**
	 * 字段 - 模糊（在包含的基础上，允许出现少数错别字，即相似词搜索）
	 * 
	 * @param cri		条件参数
	 * @param field		字段名
	 * @param value		值：%x%
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria fuzzy(Criteria cri, String field, String value) {
		return cri.and(field).fuzzy(value);
	}
	
	/**
	 * 地理空间 - 方格区域
	 * 
	 * @param field			位置字段：GeoPoint[x,y]
	 * @param leftTopX	左上角
	 * @param leftTopY
	 * @param rightBottomX	右下角
	 * @param rightBottomY
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria withinBox(String field, double leftTopX, double leftTopY, double rightBottomX, double rightBottomY) {
		return Criteria.where(field).boundedBy(new GeoBox(new GeoPoint(leftTopX, leftTopY), new GeoPoint(rightBottomX, rightBottomY)));
	}
	
	/**
	 * 地理空间 - 方格区域
	 * 
	 * @param cri			条件参数
	 * @param field			位置字段：GeoPoint[x,y]
	 * @param leftTopX	左上角
	 * @param leftTopY
	 * @param rightBottomX	右下角
	 * @param rightBottomY
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria withinBox(Criteria cri, String field, double leftTopX, double leftTopY, double rightBottomX, double rightBottomY) {
		return cri.boundedBy(new GeoBox(new GeoPoint(leftTopX, leftTopY), new GeoPoint(rightBottomX, rightBottomY)));
	}
	
	/**
	 * 地理空间 - 附近
	 * 
	 * @param field		位置字段：double[x,y]
	 * @param x			基准位置
	 * @param y
	 * @param distance	最远距离（千米）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria near(String field, double x, double y, double distance) {
		return Criteria.where(field).within(new Point(x, y), new Distance(distance, Metrics.KILOMETERS));
	}
	
	/**
	 * 地理空间 - 附近
	 * 
	 * @param cri		条件参数
	 * @param field		位置字段：double[x,y]
	 * @param x			基准位置
	 * @param y
	 * @param distance	最远距离（千米）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Criteria near(Criteria cri, String field, double x, double y, double distance) {
		return cri.and(field).within(new Point(x, y), new Distance(distance, Metrics.KILOMETERS));
	}
	
	/**
	 * ID 条件
	 * 
	 * @param id
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria id(Object id) {
		return Criteria.where(EsConstant.ID_FIELD).is(id);
	}

	/**
	 * ID 条件
	 *
	 * @param ids
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Criteria ids(Collection<?> ids) {
		return Criteria.where(EsConstant.ID_FIELD).in(ids);
	}
	
	
}
